package fr.kyvos.optimization;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

/**
 * Class SolutionBoolean
 * 
 * @file SolutionBoolean.java
 * @author fr.kyvos
 * @date 01 January 2013
 *
 */

public class SolutionBoolean {
	
	protected int mSize;					// size of the solution
	protected int mNbTrue;					// number of true values
	protected ArrayList<Boolean> mValues; 	// values
	protected double mFitness;				// fitness of the solution
	
	/**
	 * Constructor
	 * Build a solution with false values
	 * 
	 * @param pSize of the solution
	 */
	public SolutionBoolean(int pSize) {
		super();

		mSize = pSize;
		mValues = new ArrayList<Boolean>();
		for (int i=0;i<mSize;i++) {
			mValues.add(false);
		}
		mNbTrue = 0;
		mFitness = 0.0;
	}
		
	/**
	 * Constructor
	 * Build a solution with random boolean values
	 * 
	 * @param pSize of the solution
	 * @param pRandomBoolean
	 */
	public SolutionBoolean(int pSize, Random pRandomBoolean) {
		super();
		boolean lvalue;

		mSize = pSize;
		mValues = new ArrayList<Boolean>();
		mNbTrue = 0;
		for (int i=0;i<mSize;i++) {
			lvalue = pRandomBoolean.nextBoolean();
			mValues.add(lvalue);
			if (lvalue) { mNbTrue++; }
		}		
		mFitness = 0.0;
	}
		
	/**
	 * @return the size of the solution
	 */
	public int getSize() {
		return mSize;
	}

	/**
	 * @param pSize the size to set
	 */
	public void setSize(int pSize) {
		mSize = pSize;
	}

	/**
	 * @return the nbTrue "true" values of the solution
	 */
	public int getNbTrue() {
		return mNbTrue;
	}

	/**
	 * @param pNbTrue the nbTrue to set
	 */
	public void setNbTrue(int pNbTrue) {
		mNbTrue = pNbTrue;
	}

	/**
	 * @return the fitness of the solution
	 */
	public double getFitness() {
		return mFitness;
	}

	/**
	 * @param pFitness the fitness to set
	 */
	public void setFitness(double pFitness) {
		mFitness = pFitness;
	}

	/**
	 * Set pNbTrue random true values
	 * 
	 * @param pNbTrue "true" values in the solution 
	 * @param pRandomBoolean
	 */
	public void setRandomTrueValues(int pNbTrue, Random pRandomInteger) {
		int lPositionTrue;
		
		for (int i=0;i<mSize;i++) {
			mValues.set(i,false);
		}
		mNbTrue = 0;
		while (mNbTrue < pNbTrue) {
			lPositionTrue = pRandomInteger.nextInt(mSize);
			if (!mValues.get(lPositionTrue)) {
				mValues.set(lPositionTrue,true);
				mNbTrue++;
			}
		}		
	}
		
	/**
	 * Set a "true" value at position pPosition
	 * 
	 * @param pPosition
	 */
	public void setValueAtPosition(int pPosition, boolean pValue) {
		if ((pPosition >= 0) && (pPosition < mSize)) {
			if (!mValues.get(pPosition)) {
				if (pValue) { mNbTrue++; }
			}
			else {
				if (!pValue) { mNbTrue--; }
			}
			boolean success = mValues.set(pPosition, pValue);
			//System.out.println("success : "+success+"\npPosition : "+pPosition+" - pValue : "+pValue+" - mSize : "+mSize);
		}		
	}

	/**
	 * @param pPosition
	 */
	public boolean getValueAtPosition(int pPosition) {
		if ((pPosition >= 0) && (pPosition < mSize)) {
			return mValues.get(pPosition);
		}
		return false;
	}
	
	/**
	 * Copy pSolution in the current solution
	 * 
	 * @param pSolution
	 */
	public boolean copySolution(SolutionBoolean pSolution) {
		if (pSolution.mSize == mSize) {
			mSize = pSolution.mSize;
			mNbTrue = pSolution.mNbTrue;
			mFitness = pSolution.mFitness;		
			for (int i=0;i<mSize;i++) {
				mValues.set(i,pSolution.getValueAtPosition(i));
			}
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Calculate the Hamming distance between the 2 solutions
	 * 
	 * @param pSolution
	 */
	public int hammingDistance(SolutionBoolean pSolution) {
		int lHDistance;
		
		if (pSolution.mSize == mSize) {
			lHDistance = 0;		
			for (int i=0;i<mSize;i++) {
				if (mValues.get(i) != pSolution.getValueAtPosition(i)) {
					lHDistance++;
				}
			}
			return lHDistance;
		}
		else {
			return -1;
		}
	}

	/**
	 * Calculate the number common "true" values
	 * 
	 * @param pSolution
	 */
	public int communTrueValues(SolutionBoolean pSolution) {
		int lNbTrueValues;
		
		if (pSolution.mSize == mSize) {
			lNbTrueValues = 0;		
			for (int i=0;i<mSize;i++) {
				if (mValues.get(i) && pSolution.getValueAtPosition(i)) {
					lNbTrueValues++;
				}
			}
			return lNbTrueValues;
		}
		else {
			return -1;
		}
	}

	/**
     * Display the solution
     */
	public String toString() {
		StringBuilder result = new StringBuilder();
	    String newLine = System.getProperty("line.separator");
	    Iterator<Boolean> lIt = mValues.iterator();
	    
		if (mNbTrue == 0) {
			result.append("Solution (null): [");
		}
		else {
			result.append("Solution ("+mFitness+"): [");
		}
		while (lIt.hasNext()) {
			if (lIt.next().booleanValue()) { result.append(" 1"); }
			else { result.append(" 0"); }
		}
		result.append(" ]");
		result.append(newLine);
       	return result.toString();
    }
}
